import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;

import java.util.*;

public class DLC extends JDialog {
    private JE2048 maingame;
    private Container c = getContentPane();
    private JTextField[][] tf;
    private void refreshRuleArea(JTextArea rulesArea) {
        HashMap<Rule, Integer> rulemap = maingame.getRules();
        Set<Map.Entry<Rule, Integer>> rules = rulemap.entrySet();
        rulesArea.setText("");
        for (Map.Entry<Rule, Integer> entry: rules) {
            StringBuilder sb = new StringBuilder();
            int a = entry.getKey().a;
            int b = entry.getKey().b;
            int v = entry.getValue();
            sb.append(a != -2147483648 ? "" + a : "inf");
            sb.append(" + ");
            sb.append(b != -2147483648 ? "" + b : "inf");
            sb.append(" = ");
            sb.append(v != -2147483648 ? "" + v : "inf");
            sb.append(System.getProperty("line.separator"));
            rulesArea.append(sb.toString());
        }
    }
    private void refreshBoardPane(JPanel board) {
        board.removeAll();
        int n = maingame.getBoardWidth();
        int m = maingame.getBoardHeight();
        board.setLayout(new GridLayout(n, m));
        tf = new JTextField[n][m];
        int[][] curBoard = maingame.getBoard();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                tf[i][j] = new JTextField();
                if (curBoard[i][j] != -2147483648) tf[i][j].setText("" + curBoard[i][j]);
                else tf[i][j].setText("inf");
                board.add(tf[i][j]);
            }
        }
    }
    private void refreshCustomTextArea(JTextArea customTextArea) {
        HashMap<Integer, String> map = maingame.getTextMapping();
        customTextArea.setText("");
        for (Map.Entry<Integer, String> entry: map.entrySet()) {
            StringBuilder sb = new StringBuilder();
            int i = entry.getKey();
            sb.append(i != -2147483648 ? "" + i : "inf");
            sb.append(" -> ");
            sb.append(entry.getValue());
            sb.append(System.getProperty("line.separator"));
            customTextArea.append(sb.toString());
        }
    }
    private int parseUIntOrInf(String text) throws Exception {
        int res = -2147483648;
        if (!text.equals("inf")) {
            res = Integer.parseInt(text);
            if (res < 0) throw new Exception();
        }
        return res;
    }
    private String tohex(int byt) {
        int hi = byt / 16, lo = byt % 16;
        if (hi < 10) hi += '0';
        else hi += 'A' - 10;
        if (lo < 10) lo += '0';
        else lo += 'A' - 10;
        return new String(new char[] {(char) hi, (char) lo});
    }
    private String toRGB(Color c) {
        return "#" + tohex(c.getRed()) + tohex(c.getGreen()) + tohex(c.getBlue());
    }
    private void refreshCustomColorArea(JTextArea customColorArea) {
        HashMap<Integer, Color> map = maingame.getColorMapping();
        customColorArea.setText("");
        for (Map.Entry<Integer, Color> entry: map.entrySet()) {
            StringBuilder sb = new StringBuilder();
            int i = entry.getKey();
            sb.append(i != -2147483648 ? "" + i : "inf");
            sb.append(" -> ");
            sb.append(toRGB(entry.getValue()));
            sb.append(System.getProperty("line.separator"));
            customColorArea.append(sb.toString());
        }
    }
    private void initialize() {
        boolean autoplaying = maingame.isAutoPlaying();
        JPanel history = new JPanel();
        history.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "游戏控制"));
        JButton restart = new JButton("重新开始");
        JButton undo = new JButton("撤销");
        JButton redo = new JButton("重做");
        history.add(restart);
        history.add(undo);
        history.add(redo);
        c.add(history);
        JLabel width = new JLabel("长：");
        JTextField a = new JTextField(20);
        JLabel height = new JLabel("宽：");
        JTextField b = new JTextField(20);
        JButton update = new JButton("更新");
        update.setEnabled(!autoplaying);
        JPanel size = new JPanel();
        size.add(width);
        size.add(a);
        size.add(height);
        size.add(b);
        size.add(update);
        size.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "修改盘面大小"));
        c.add(size);
        JLabel currentRules = new JLabel("现存合成规则（排名不分先后）：");
        JTextArea rulesArea = new JTextArea(20, 20);
        JScrollPane sp = new JScrollPane(rulesArea);
        sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        rulesArea.setEditable(false);
        JTextField lhsTF = new JTextField(20);
        JLabel plus = new JLabel("+");
        JTextField rhsTF = new JTextField(20);
        JLabel eq = new JLabel("=");
        JTextField resultTF = new JTextField(20);
        JButton appendRule = new JButton("添加规则（若结果为0则为删除）");
        appendRule.setEnabled(!autoplaying);
        JButton saveRule = new JButton("保存当前规则");
        JButton loadRule = new JButton("读取规则...");
        loadRule.setEnabled(!autoplaying);
        JPanel customRule = new JPanel();
        customRule.setLayout(new BoxLayout(customRule, BoxLayout.Y_AXIS));
        customRule.add(currentRules);
        customRule.add(sp);
        JPanel addRulePanel = new JPanel();
        addRulePanel.add(lhsTF);
        addRulePanel.add(plus);
        addRulePanel.add(rhsTF);
        addRulePanel.add(eq);
        addRulePanel.add(resultTF);
        addRulePanel.add(appendRule);
        addRulePanel.add(saveRule);
        addRulePanel.add(loadRule);
        customRule.add(addRulePanel);
        customRule.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "自定义合成规则"));
        c.add(customRule);
        refreshRuleArea(rulesArea);
        JPanel board = new JPanel();
        refreshBoardPane(board);
        JButton adjust = new JButton("修改");
        adjust.setEnabled(!autoplaying);
        JPanel changeBoard = new JPanel();
        changeBoard.setLayout(new BoxLayout(changeBoard, BoxLayout.Y_AXIS));
        changeBoard.add(board);
        JPanel buttonsPane = new JPanel();
        buttonsPane.add(adjust);
        changeBoard.add(buttonsPane);
        changeBoard.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "修改盘面数字"));
        c.add(changeBoard);
        JButton random = new JButton("纯随机");
        random.setEnabled(!autoplaying);
        JButton swirl = new JButton("滚筒洗衣机");
        swirl.setEnabled(!autoplaying);
        JButton corner = new JButton("左下角优先");
        corner.setEnabled(!autoplaying);
        JButton greedy = new JButton("贪心法");
        greedy.setEnabled(!autoplaying);
        JButton monte = new JButton("你打 2048 像音游");
        monte.setEnabled(!autoplaying);
        JPanel autoplay = new JPanel();
        autoplay.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "奥拓普雷先生的 2048（Autoplay）"));
        autoplay.add(random);
        autoplay.add(swirl);
        autoplay.add(corner);
        autoplay.add(greedy);
        autoplay.add(monte);
        c.add(autoplay);
        JPanel customText = new JPanel();
        customText.setLayout(new BoxLayout(customText, BoxLayout.Y_AXIS));
        JTextArea ta = new JTextArea(20, 20);
        ta.setEditable(false);
        refreshCustomTextArea(ta);
        JScrollPane tasp = new JScrollPane(ta);
        tasp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        tasp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        customText.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "自定义方块文本"));
        customText.add(tasp);
        JPanel addCustomText = new JPanel();
        JLabel number = new JLabel("数字：");
        JTextField tfNumber = new JTextField(20);
        JLabel text = new JLabel("对应文字：");
        JTextField tfText = new JTextField(20);
        JButton updateText = new JButton("修改");
        JButton readCustomText = new JButton("读取自定义文本列表...");
        JButton saveCustomText = new JButton("保存自定义文本列表");
        addCustomText.add(number);
        addCustomText.add(tfNumber);
        addCustomText.add(text);
        addCustomText.add(tfText);
        addCustomText.add(updateText);
        addCustomText.add(saveCustomText);
        addCustomText.add(readCustomText);
        customText.add(addCustomText);
        c.add(customText);
        JPanel customColor = new JPanel();
        customColor.setLayout(new BoxLayout(customColor, BoxLayout.Y_AXIS));
        JTextArea customColorArea = new JTextArea(20, 20);
        customColorArea.setEditable(false);
        refreshCustomColorArea(customColorArea);
        JScrollPane ccasp = new JScrollPane(customColorArea);
        ccasp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        ccasp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        customColor.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "自定义方块颜色"));
        customColor.add(ccasp);
        JPanel addCustomColor = new JPanel();
        JLabel numberColor = new JLabel("数字：");
        JTextField tfNumberColor = new JTextField(20);
        JLabel color = new JLabel("对应颜色（请在下方编辑）");
        JPanel colorEditor = new JPanel();
        colorEditor.setLayout(new BoxLayout(colorEditor, BoxLayout.Y_AXIS));
        colorEditor.setBorder(BorderFactory.createTitledBorder(BorderFactory.createBevelBorder(1), "颜色编辑器"));
        JLabel redLabel = new JLabel("R: ");
        JTextField redTF = new JTextField(20);
        JPanel red = new JPanel();
        red.add(redLabel); red.add(redTF); redTF.setText("0");
        JLabel greenLabel = new JLabel("G: ");
        JTextField greenTF = new JTextField(20);
        JPanel green = new JPanel();
        green.add(greenLabel); green.add(greenTF); greenTF.setText("0");
        JLabel blueLabel = new JLabel("B: ");
        JTextField blueTF = new JTextField(20);
        JPanel blue = new JPanel();
        blue.add(blueLabel); blue.add(blueTF); blueTF.setText("0");
        JTextField colorShower = new JTextField();
        colorShower.setEditable(false);
        colorShower.setBorder(null);
        colorShower.setBackground(Color.BLACK);
        JButton confirm = new JButton("确认");
        colorEditor.add(red);
        colorEditor.add(green);
        colorEditor.add(blue);
        colorEditor.add(colorShower);
        colorEditor.add(confirm);
        redTF.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void removeUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!redTF.getText().equals("")) {
                        i = Integer.parseInt(redTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(i, c.getGreen(), c.getBlue())); 
                } catch (Exception ex) {}
            }
            @Override
            public void insertUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!redTF.getText().equals("")) {
                        i = Integer.parseInt(redTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(i, c.getGreen(), c.getBlue())); 
                } catch (Exception ex) {}
            }
            @Override
            public void changedUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!redTF.getText().equals("")) {
                        i = Integer.parseInt(redTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(i, c.getGreen(), c.getBlue())); 
                } catch (Exception ex) {}
            }
        });
        greenTF.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void removeUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!greenTF.getText().equals("")) {
                        i = Integer.parseInt(greenTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), i, c.getBlue())); 
                } catch (Exception ex) {}
            }
            @Override
            public void insertUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!greenTF.getText().equals("")) {
                        i = Integer.parseInt(greenTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), i, c.getBlue())); 
                } catch (Exception ex) {}
            }
            @Override
            public void changedUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!greenTF.getText().equals("")) {
                        i = Integer.parseInt(greenTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), i, c.getBlue())); 
                } catch (Exception ex) {}
            }
        });
        blueTF.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void removeUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!blueTF.getText().equals("")) {
                        i = Integer.parseInt(blueTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), c.getGreen(), i)); 
                } catch (Exception ex) {}
            }
            @Override
            public void insertUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!blueTF.getText().equals("")) {
                        i = Integer.parseInt(blueTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), c.getGreen(), i)); 
                } catch (Exception ex) {}
            }
            @Override
            public void changedUpdate(DocumentEvent e) {
                try {
                    int i = 0;
                    if (!blueTF.getText().equals("")) {
                        i = Integer.parseInt(blueTF.getText());
                        if (i < 0) throw new Exception();
                    }
                    Color c = colorShower.getBackground();
                    colorShower.setBackground(new Color(c.getRed(), c.getGreen(), i)); 
                } catch (Exception ex) {}
            }
        });
        confirm.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Color c = colorShower.getBackground();
                String corrText = tfText.getText();
                try {
                    int corrNum = parseUIntOrInf(tfNumberColor.getText());
                    HashMap<Integer, Color> colormap = maingame.getColorMapping();
                    colormap.put(corrNum, c);
                    refreshCustomColorArea(customColorArea);
                    tfNumberColor.setText("");
                    maingame.refreshGUI();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, "2048里只有正整数和无穷大呀", "李在赣神魔", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
        JButton readCustomColor = new JButton("读取自定义颜色列表...");
        JButton saveCustomColor = new JButton("保存自定义颜色列表");
        addCustomColor.add(numberColor);
        addCustomColor.add(tfNumberColor);
        addCustomColor.add(color);
        addCustomColor.add(saveCustomColor);
        addCustomColor.add(readCustomColor);
        customColor.add(addCustomColor);
        customColor.add(colorEditor);
        c.add(customColor);
        saveCustomColor.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("颜色列表文件（*.clst）", "clst"));
                int opt = fileChooser.showSaveDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    FileUtil.saveFile(fileChooser.getSelectedFile(), customColorArea.getText(), "UTF-8");
                }
            }
        });
        readCustomColor.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("颜色列表文件（*.clst）", "clst"));
                int opt = fileChooser.showOpenDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    String content = FileUtil.readFile(fileChooser.getSelectedFile(), "UTF-8");
                    String[] lines = content.split(System.getProperty("line.separator"));
                    for (int i = 0; i < lines.length; i++) {
                        String l = lines[i];
                        int number = 0;
                        int stage = 0;
                        int inf_i = 0, inf_in = 0, inf_inf = 0;
                        Color color = Color.BLACK;
                        for (int j = 0; j < l.length(); j++) {
                            char c = l.charAt(j);
                            if (c >= '0' && c <= '9' && stage == 0) number = number * 10 + c - '0';
                            else if (c == 'i') inf_i = 1;
                            else if (c == 'n') inf_in = inf_i + 1;
                            else if (c == 'f' && (j == l.length() - 1 || l.charAt(j + 1) == ' ' || l.charAt(j + 1) == '+' || l.charAt(j + 1) == '=' || l.charAt(j + 1) == '\r' || l.charAt(j + 1) == '\n')) inf_inf = inf_in + 1;
                            else if (c == ' ') continue;
                            else if (c == '#') {
                                stage++;
                            }
                            else if (stage == 1) {
                                int rhi = c;
                                j++;
                                int rlo = l.charAt(j);
                                j++;
                                int ghi = l.charAt(j);
                                j++;
                                int glo = l.charAt(j);
                                j++;
                                int bhi = l.charAt(j);
                                j++;
                                int blo = l.charAt(j);
                                j++;
                                rhi = rhi >= 'A' ? rhi - 'A' + 10 : rhi - '0';
                                rlo = rlo >= 'A' ? rlo - 'A' + 10 : rlo - '0';
                                ghi = ghi >= 'A' ? ghi - 'A' + 10 : ghi - '0';
                                glo = glo >= 'A' ? glo - 'A' + 10 : glo - '0';
                                bhi = bhi >= 'A' ? bhi - 'A' + 10 : bhi - '0';
                                blo = blo >= 'A' ? blo - 'A' + 10 : blo - '0';
                                color = new Color(rhi * 16 + rlo, ghi * 16 + glo, bhi * 16 + blo);
                            }
                            if (inf_inf == 3) {
                                inf_i = inf_in = inf_inf = 0;
                                number = -2147483648;
                            }
                        }
                        HashMap<Integer, Color> colormap = maingame.getColorMapping();
                        colormap.put(number, color);
                        stage = 0;
                    }
                    refreshCustomTextArea(ta);
                    maingame.refreshGUI();
                }
            }
        });
        updateText.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String corrText = tfText.getText();
                try {
                    int corrNum = parseUIntOrInf(tfNumber.getText());
                    HashMap<Integer, String> textmap = maingame.getTextMapping();
                    textmap.put(corrNum, corrText);
                    refreshCustomTextArea(ta);
                    tfNumber.setText("");
                    tfText.setText("");
                    maingame.refreshGUI();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, "2048里只有正整数和无穷大呀", "李在赣神魔", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
        saveCustomText.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("文本列表文件（*.lst）", "lst"));
                int opt = fileChooser.showSaveDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    FileUtil.saveFile(fileChooser.getSelectedFile(), ta.getText(), "UTF-8");
                }
            }
        });
        readCustomText.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("文本列表文件（*.lst）", "lst"));
                int opt = fileChooser.showOpenDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    String content = FileUtil.readFile(fileChooser.getSelectedFile(), "UTF-8");
                    String[] lines = content.split(System.getProperty("line.separator"));
                    for (int i = 0; i < lines.length; i++) {
                        String l = lines[i];
                        int number = 0;
                        int stage = 0;
                        int inf_i = 0, inf_in = 0, inf_inf = 0;
                        StringBuilder sb = new StringBuilder();
                        for (int j = 0; j < l.length(); j++) {
                            char c = l.charAt(j);
                            if (c >= '0' && c <= '9' && stage == 0) number = number * 10 + c - '0';
                            else if (c == 'i') inf_i = 1;
                            else if (c == 'n') inf_in = inf_i + 1;
                            else if (c == 'f' && (j == l.length() - 1 || l.charAt(j + 1) == ' ' || l.charAt(j + 1) == '+' || l.charAt(j + 1) == '=' || l.charAt(j + 1) == '\r' || l.charAt(j + 1) == '\n')) inf_inf = inf_in + 1;
                            else if (c == ' ') continue;
                            else if (c == '-' && l.charAt(j + 1) == '>') {
                                stage++;
                                j += 2;
                            }
                            else if (stage == 1) {
                                sb.append(c);
                            }
                            if (inf_inf == 3) {
                                inf_i = inf_in = inf_inf = 0;
                                number = -2147483648;
                            }
                        }
                        HashMap<Integer, String> textmap = maingame.getTextMapping();
                        textmap.put(number, sb.toString());
                        stage = 0;
                    }
                    refreshCustomTextArea(ta);
                    maingame.refreshGUI();
                }
            }
        });
        abstract class Autoplayer implements ActionListener {
            public abstract void aiLogic();
            public void aiLogicAfter() {
                aiLogic();
            }
            public void actionPerformed(ActionEvent e) {
                new Thread(new Runnable() {
                    public void run() {
                        maingame.setAutoplaying();
                        update.setEnabled(false);
                        appendRule.setEnabled(false);
                        loadRule.setEnabled(false);
                        adjust.setEnabled(false);
                        random.setEnabled(false);
                        swirl.setEnabled(false);
                        corner.setEnabled(false);
                        monte.setEnabled(false);
                        greedy.setEnabled(false);
                        boolean won = false;
                        while (!maingame.judgeLose() && !(won = maingame.judgeWin())) aiLogic();
                        if (maingame.judgeLose()) {
                            JOptionPane.showMessageDialog(null, "奥拓普雷先生不会玩 2048", "好似", JOptionPane.ERROR_MESSAGE);
                        } else if (won) {
                            Object[] options = {"继续", "重开"};
                            int n = JOptionPane.showOptionDialog(null, "恭喜奥拓普雷先生再杀一曲……等等这是什么？", "nb", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options);
                            if (n == 1) {
                                maingame.reInitBoard();
                            } else {
                                while (!maingame.judgeLose()) aiLogicAfter();
                                JOptionPane.showMessageDialog(null, "想你了奥拓普雷", "牢大坠机了", JOptionPane.ERROR_MESSAGE);
                            }
                        }
                        maingame.reInitBoard();
                        update.setEnabled(true);
                        appendRule.setEnabled(true);
                        loadRule.setEnabled(true);
                        adjust.setEnabled(true);
                        random.setEnabled(true);
                        swirl.setEnabled(true);
                        corner.setEnabled(true);
                        monte.setEnabled(true);
                        greedy.setEnabled(true);
                    }
                }).start();
            }
        }
        random.addActionListener(new Autoplayer() {
            @Override
            public void aiLogic() {
                ArrayList<Integer> moveableDirections = maingame.getMoveableDirections();
                int randomDir = new Random(System.currentTimeMillis()).nextInt(moveableDirections.size());
                maingame.moveAtDirection(moveableDirections.get(randomDir));
            }
        });
        swirl.addActionListener(new Autoplayer() {
            @Override
            public void aiLogic() {
                int dir = 0;
                while (!maingame.judgeLose() && !maingame.judgeWin()) {
                    maingame.moveAtDirection(dir);
                    dir = (dir + 1) % 4;
                }
            }
            @Override
            public void aiLogicAfter() {
                int dir = 0;
                while (!maingame.judgeLose()) {
                    maingame.moveAtDirection(dir);
                    dir = (dir + 1) % 4;
                }
            }
        });
        corner.addActionListener(new Autoplayer() {
            @Override
            public void aiLogic() {
                boolean status = maingame.moveAtDirection(3);
                boolean status2 = maingame.moveAtDirection(2);
                if (!status) {
                    boolean status3 = maingame.moveAtDirection(1);
                    maingame.moveAtDirection(3);
                    if (!status3) {
                        maingame.moveAtDirection(0);
                        maingame.moveAtDirection(2);
                    }
                }
            }
        });
        monte.addActionListener(new Autoplayer() {
            @Override
            public void aiLogic() {
                class MonteCarloAI {
                    class Prediction {
                        private int action;
                        private double score;
                        public Prediction(int action, double score) {
                            this.action = action;
                            this.score = score;
                        }
                        public int getAction() {
                            return action;
                        }
                        public double getScore() {
                            return score;
                        }
                    }
                    int pred_per_move, pred_depth;
                    Prediction best_pred = new Prediction(0, 0.0);
                    Random r = new Random(System.currentTimeMillis());
                    public MonteCarloAI(int pred_per_move, int pred_depth) {
                        this.pred_per_move = pred_per_move;
                        this.pred_depth = pred_depth;
                    }
                    public void run() {
                        ArrayList<Integer> initialMoveableDirections = maingame.getMoveableDirections();
                        for (int i = 0; i < 4; i++) {
                            if (!initialMoveableDirections.contains(i)) continue;
                            int total = 0;
                            for (int j = 0; j < pred_per_move; j++) {
                                int n = maingame.getBoardWidth(), m = maingame.getBoardHeight();
                                int[][] board = maingame.getBoard();
                                int[][] backupBoard = new int[n][m];
                                for (int k = 0; k < n; k++) {
                                    for (int l = 0; l < m; l++) {
                                        backupBoard[k][l] = board[k][l];
                                    }
                                }
                                int backupScore = maingame.getScore();
                                maingame.moveAtDirectionWithoutRefresh(i);
                                maingame.judgeLose();
                                ArrayList<Integer> moveableDirections = maingame.getMoveableDirections();
                                for (int move_depth = 0; move_depth <= pred_depth && !maingame.judgeLose(); move_depth++) {
                                    int randomDir = r.nextInt(moveableDirections.size());
                                    randomDir = moveableDirections.get(randomDir);
                                    maingame.moveAtDirectionWithoutRefresh(randomDir);
                                    moveableDirections = maingame.getMoveableDirections();
                                }
                                total += maingame.getScore();
                                maingame.setScore(backupScore);
                                for (int k = 0; k < n; k++) {
                                    for (int l = 0; l < m; l++) {
                                        maingame.setBoardAtWithoutRefresh(k, l, backupBoard[k][l]);
                                    }
                                }
                            }
                            double avg_score = total * 1.0 / pred_per_move;
                            if (avg_score > best_pred.getScore()) {
                                best_pred = new Prediction(i, avg_score);
                            }
                        }
                    }
                    public int getBestAction() {
                        return best_pred.getAction();
                    }
                }
                MonteCarloAI mcm = new MonteCarloAI(500, 40);
                mcm.run();
                maingame.moveAtDirection(mcm.getBestAction());
            }
        });
        greedy.addActionListener(new Autoplayer() {
            @Override
            public void aiLogic() {
                ArrayList<Integer> moveableDirections = maingame.getMoveableDirections();
                int n = maingame.getBoardWidth(), m = maingame.getBoardHeight();
                int[][] board = maingame.getBoard();
                int[][] backupBoard = new int[n][m];
                for (int k = 0; k < n; k++) {
                    for (int l = 0; l < m; l++) {
                        backupBoard[k][l] = board[k][l];
                    }
                }
                int backupScore = maingame.getScore();
                int maxscore = 0;
                int maxdir = 0;
                for (int dir: moveableDirections) {
                    maingame.moveAtDirectionWithoutRefresh(dir);
                    int score = maingame.getScore();
                    if (score > maxscore) {
                        maxscore = score;
                        maxdir = dir;
                    }
                    for (int k = 0; k < n; k++) {
                        for (int l = 0; l < m; l++) {
                            maingame.setBoardAtWithoutRefresh(k, l, backupBoard[k][l]);
                        }
                    }
                }
                maingame.setScore(backupScore);
                maingame.moveAtDirection(maxdir);
            }
        });
        update.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    int a1 = Integer.parseInt(a.getText());
                    int b1 = Integer.parseInt(b.getText());
                    if (a1 < 0 || b1 < 0) throw new Exception();
                    maingame.setBoardSize(b1, a1);
                    maingame.reInitBoard();
                    refreshRuleArea(rulesArea);
                    DLC.this.dispose();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, "长和宽必须是正整数", "李在赣神魔", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
        appendRule.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    int a = parseUIntOrInf(lhsTF.getText());
                    int b = parseUIntOrInf(rhsTF.getText());
                    int c = parseUIntOrInf(resultTF.getText());
                    if (c != 0) {
                        maingame.addRule(a, b, c);
                        maingame.addRule(b, a, c);
                    } else {
                        maingame.removeRule(a, b);
                        maingame.removeRule(b, a);
                    }
                    refreshRuleArea(rulesArea);
                    lhsTF.setText("");
                    rhsTF.setText("");
                    resultTF.setText("");
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, "2048里只有正整数和无穷大相加呀", "李在赣神魔", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
        saveRule.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("合成规则文件（*.rul）", "rul"));
                int opt = fileChooser.showSaveDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    FileUtil.saveFile(fileChooser.getSelectedFile(), rulesArea.getText(), "UTF-8");
                }
            }
        });
        loadRule.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setCurrentDirectory(new java.io.File("."));
                fileChooser.setFileFilter(new FileNameExtensionFilter("合成规则文件（*.rul）", "rul"));
                int opt = fileChooser.showOpenDialog(c);
                if (opt == JFileChooser.APPROVE_OPTION) {
                    String content = FileUtil.readFile(fileChooser.getSelectedFile(), "UTF-8");
                    String[] lines = content.split(System.getProperty("line.separator"));
                    for (int i = 0; i < lines.length; i++) {
                        String l = lines[i];
                        int[] rule = new int[3];
                        int stage = 0;
                        int inf_i = 0, inf_in = 0, inf_inf = 0;
                        for (int j = 0; j < l.length(); j++) {
                            char c = l.charAt(j);
                            if (c >= '0' && c <= '9') rule[stage] = rule[stage] * 10 + c - '0';
                            else if (c == 'i') inf_i = 1;
                            else if (c == 'n') inf_in = inf_i + 1;
                            else if (c == 'f' && (j == l.length() - 1 || l.charAt(j + 1) == ' ' || l.charAt(j + 1) == '+' || l.charAt(j + 1) == '=' || l.charAt(j + 1) == '\r' || l.charAt(j + 1) == '\n')) inf_inf = inf_in + 1;
                            else if (c == ' ') continue;
                            else if (c == '+' || c == '=') stage++;
                            if (inf_inf == 3) {
                                inf_i = inf_in = inf_inf = 0;
                                rule[stage] = -2147483648;
                            }
                        }
                        maingame.addRule(rule[0], rule[1], rule[2]);
                        if (rule[0] != rule[1]) maingame.addRule(rule[1], rule[0], rule[2]);
                        stage = 0;
                    }
                    refreshRuleArea(rulesArea);
                }
            }
        });
        adjust.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean errored = false;
                maingame.reInitBoard();
                for (int i = 0; i < maingame.getBoardWidth(); i++) {
                    for (int j = 0; j < maingame.getBoardHeight(); j++) {
                        try {
                            int num = parseUIntOrInf(tf[i][j].getText());
                            maingame.setBoardAt(i, j, num);
                        } catch (Exception ex) {
                            if (!errored) JOptionPane.showMessageDialog(null, "2048里只有正整数和无穷大呀", "李在赣神魔", JOptionPane.ERROR_MESSAGE);
                            errored = true;
                        }
                    }
                }
                if (!errored) DLC.this.dispose();
            }
        });
        restart.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                maingame.reInitBoard();
            }
        });
        undo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                maingame.undo();
            }
        });
        redo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                maingame.redo();
            }
        });
        JScrollPane wrapAround = new JScrollPane(c);
        wrapAround.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        wrapAround.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        setContentPane(wrapAround);
    }
    public DLC(JE2048 maingame) {
        this.maingame = maingame;
        setTitle("高级设置");
        setSize(1366, 768);
        setVisible(true);
        c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS));
        initialize();
    }
}